package dp.mobile.game;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;

import javax.microedition.khronos.opengles.GL10;

import org.w3c.dom.Node;

import android.graphics.Point;
import android.util.Log;
import dp.mobile.entity.Bullet;
import dp.mobile.entity.Enemy;
import dp.mobile.entity.Entity;
import dp.mobile.entity.generator.Generator;
import dp.mobile.entity.generator.GeneratorData;
import dp.mobile.entity.generator.GeneratorFactory;
import dp.mobile.entity.saveable.SaveableGame;
import dp.mobile.entity.saveable.SaveableGenerator;
import dp.mobile.entity.saveable.SaveableTower;
import dp.mobile.entity.tower.Tower;
import dp.mobile.entity.tower.TowerData;
import dp.mobile.entity.tower.TowerFactory;
import dp.mobile.glwrapper.GameRenderer;
import dp.mobile.level.Dialog;
import dp.mobile.level.Level;
import dp.mobile.level.Map;
import dp.mobile.system.SoundManager;
import dp.mobile.system.Utilities;
import dp.mobile.system.XMLReader;

public class Game extends Thread implements Serializable {
	private static final long serialVersionUID = -8338759690805250869L;
	private static final String TAG = "Game";
	
	public Game(String levelSource) {
		initLevel(levelSource);
		
		// restart view
		Point offset = GameRenderer.instance().getOffset();
		offset.set(-Utilities.SCREEN_WIDTH / 2, Utilities.SCREEN_HEIGHT / 2);
		GameRenderer.instance().setDepth(Utilities.MAX_DEPTH);
	}
	
	public static Game createResumeGame() {
		Game retval = null;
		
		File saveFile = new File(Utilities.SAVE_FILE);
		if (saveFile.exists()) {
			try {
				FileInputStream fis = new FileInputStream(saveFile);
				
				ObjectInputStream stream = new ObjectInputStream(fis);
				
				SaveableGame game = (SaveableGame) stream.readObject();
				
				if (game.mLevelSource.equals("tutorial.xml"))
					retval = new TutorialGame(game.mLevelSource);
				else
					retval = new Game(game.mLevelSource);
				
				
				for (SaveableGenerator g : game.mGenerators) {
					Generator G = new Generator(GeneratorFactory.instance().getGeneratorData(g.mPowerLevel, g.mRangeLevel, g.mVoltageLevel), retval.mPlayer, g.mTileX, g.mTileY, retval.getElapsedGameTime(), false);
					G.setID(g.mId);
					retval.addGenerator(G, true);
					Log.i("this" , "ADD GEN");
				}
				
				for (SaveableTower t : game.mTowers) {
					Tower T = new Tower(TowerFactory.instance().getTowerData(t.mTowerDataIndex), retval.mPlayer, t.mTileX, t.mTileY, retval.getElapsedGameTime(), false);
					T.setID(t.mId);
					T.setAngle(t.mAngle);
					retval.addTower(T, true);
					Log.i("this" , "ADD TOWER");
					
					for (Integer id : t.mGenerators) {
						Generator target = null;
						for (Generator g : retval.mGenerators) {
							if (id.equals(g.getID())) {
								target = g;
							}
						}
						if (target != null) {
							T.addGenerator(target);
						}
					}
				}
				
				GameRenderer.instance().invalidateAllTextures();
				GameRenderer.instance().invalidateVBO();
				
				retval.mPlayer.initFromSaveable(game.mPlayer);
				retval.mLevel.getWave(retval.mPlayer.getWave()).loadEnemyImage();
				
				stream.close();
				fis.close();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
		
		return retval;
	}
	
	public ArrayList<Tower> getTowers() {
		return mTowers;
	}
	
	public boolean isPaused() {
		return mPaused;
	}
	
	public void showPanel() {
		mListener.onPanelShow();	
		afterShowPanel();
	}
	
	@Override
	public void run() {
		//Get current time
		long current 	= System.currentTimeMillis();
		long difference	= 0;
		mStartGameTime = current;
		
		mRunning = true;
		
		this.save();
		// cek dialog saat game baru mulai
		checkDialog();

		// update resource once
		mListener.onElectricityChange(mPlayer.getElectricity());
    	mListener.onLifeChange(mPlayer.getLife());
    	for (int i = 0; i < 3; ++i)
    		mComponents[i] = mPlayer.getComponents(i);
    	mListener.onComponentChange(mComponents);

		//While still running
		while (mRunning) {
			//Sleep if difference less than frame time
			difference = System.currentTimeMillis() - current;
			if (difference < (1000 / Utilities.GAME_FPS)) {
				try {
					Thread.sleep((1000 / Utilities.GAME_FPS) - difference);
				} catch (InterruptedException ex) {}
			}
			
			//Save current time
			difference 	= System.currentTimeMillis() - current;
			current 	= System.currentTimeMillis();
			
			//Update and draw top state
			if(!mPaused) {
				synchronized (GameRenderer.instance()) {
					try {
						update(difference);
					} catch (GameException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
		
		Log.d(TAG, "Berhasil menghabiskan game");
	}
	
	public long getElapsedGameTime() {
		return System.currentTimeMillis() - mStartGameTime;
	}
	
	protected final void initLevel(String levelSource){
		//Initialize
		//mTimer			= null;
		//Initialize vectors
		mLevelSource		= levelSource;
		mTowers				= new ArrayList<Tower>();
		mSoldTowers			= new ArrayList<Tower>();
		mDeletedTowers		= new ArrayList<Tower>();
		mGenerators 		= new ArrayList<Generator>();
		mSoldGenerators 	= new ArrayList<Generator>();
		mDeletedGenerators 	= new ArrayList<Generator>();
		mBullets			= new ArrayList<Bullet>();
		mNewBullets			= new ArrayList<Bullet>();
		mInactiveBullets	= new ArrayList<Bullet>();
		mEnemies			= new ArrayList<Enemy>();
		mDeletedEnemies		= new ArrayList<Enemy>();
		
		// floating text
		mFloatingText			= new ArrayList<FloatingText>();
		mDeletedFloatingText	= new ArrayList<FloatingText>();

		//Create level
		mLevel 	= new Level(XMLReader.instance().parseXML(Utilities.LEVEL_FOLDER + "/" + levelSource));
		mMap	= mLevel.getMap(); 
		mNodes	= mMap.getNodes();
		mComponents = new int[] {0, 0, 0};
		
		mPlayer	= new Player(mLevel , 0);

		//Load enemy image
		mLevel.getWave(mPlayer.getWave()).loadEnemyImage();

		mDrawStartX = 0;
		mDrawStartY = 0;
		mDrawEndX	= mMap.getWidth();
		mDrawEndY	= mMap.getHeight();
		
		//Initialize nodes
		initNodesOwner();
	}
	
	protected void initNodesOwner() {
		for (dp.mobile.entity.Node node : mNodes) {
			node.setPlayer(getPlayer());
		}
	}
	
	protected void checkTower(long time) {
		//For each tower
		for (int i = 0; i < mTowers.size(); i++)
			mTowers.get(i).update(time, mEnemies);
		
		//For each sold tower
		for (int i = 0; i < mSoldTowers.size(); i++) {
			if (!mSoldTowers.get(i).isInProgress(getElapsedGameTime())) {
				Tower sold = mSoldTowers.get(i);
				mTowers.remove(sold);
				mDeletedTowers.add(sold);
				// add resource, since it's dismantled
				int k = 0;
				for (int j = 2; j >=0; --j) {
					mPlayer.addComponent(j, sold.getData().getWorth(j));
					if (sold.getData().getWorth(j) > 0) {
						mFloatingText.add(
							new FloatingText("" + sold.getData().getWorth(j)
							, Utilities.COMPONENT_COLOR[j], sold.getX() + sold.getData().getImage().Width / 2, 
							sold.getY() - 8 - 20 * k, 
							getElapsedGameTime()));
						++k;
					}
				}
			}
		}
		
		for (int i = 0; i < mDeletedTowers.size(); ++i) {
			mSoldTowers.remove(mDeletedTowers.get(i));
		}
		
		mDeletedTowers.clear();
	}
	
	protected void checkGenerator(long time) {
		// for each generator
		for (int i = 0; i < mGenerators.size(); ++i) {
			Generator generator = mGenerators.get(i);
			generator.update(time);
			if (generator.haveJustGenerateElectricity()) {
				mFloatingText.add(new FloatingText("" + generator.getData().getRegen(), Utilities.ELECTRICITY_COLOR, generator.getX() + generator.getData().getImage().Width / 2, generator.getY() - 8, getElapsedGameTime()));
			}
		}
		
		//For each sold tower
		for (int i = 0; i < mSoldGenerators.size(); i++) {
			if (!mSoldGenerators.get(i).isInProgress(getElapsedGameTime())) {
				Generator sold = mSoldGenerators.get(i);
				mGenerators.remove(sold);
				mDeletedGenerators.add(sold);
				// add resource, since it's dismantled
				int k = 0;
				for (int j = 2; j >=0; --j) {
					mPlayer.addComponent(j, sold.getData().getWorth(j));
					if (sold.getData().getWorth(j) > 0) {
						mFloatingText.add(
							new FloatingText("" + sold.getData().getWorth(j)
							, Utilities.COMPONENT_COLOR[j], sold.getX() + sold.getData().getImage().Width / 2, 
							sold.getY() - 8 - 20 * k, 
							getElapsedGameTime()));
						++k;
					}
				}
			}
		}
		
		for (int i = 0; i < mDeletedGenerators.size(); ++i) {
			mSoldGenerators.remove(mDeletedGenerators.get(i));
		}
		
		mDeletedGenerators.clear();
	}
	
	protected void checkNode(long time) {
		// for each node
		for (int i = 0; i < mNodes.size(); ++i) {
			// check to all generators
			dp.mobile.entity.Node node = mNodes.get(i);
			if (node.isOnline()) continue;
			
			for (int j = 0; j < mGenerators.size(); ++j) {
				if (node.getPlayer().equals(getPlayer()) && mGenerators.get(j).inRange(node) && !mGenerators.get(j).isInProgress(getElapsedGameTime()) && mGenerators.get(j).getPlayer().equals(getPlayer())) {
					node.setOnline(true);
					TowerData data = TowerFactory.instance().getBasicTower();
					addTower(new Tower(data, mPlayer, node.getTileX(), node.getTileY(), getElapsedGameTime(), true));
				}
			}
		}
	}
	
	protected void checkEnemy(long time) throws GameException {
		//For each enemy
		mDeletedEnemies.clear();
		for (int i = 0; i < mEnemies.size(); ++i) {
			Enemy enemy = mEnemies.get(i);
			//Update enemy
			enemy.update(time);

			//if dead
			if (isEnemyDeleted(enemy)) {
				mDeletedEnemies.add(enemy);
			}
			
			if(mPlayer.getLife() <= 0){
                //onFinish();
                endGame();
            }
		}

		//Removed deleted enemies
		deleteEnemies(mDeletedEnemies);

		//Spawn enemy
		spawnEnemies(mLevel.getWave(mPlayer.getWave()).spawnEnemy(time));
	}
	
	protected boolean isEnemyDeleted(Enemy enemy) {
		boolean retval = false;
		if (enemy.getHealth() <= 0) {
			//Gain gold
			if (!enemy.isActualDead) {
				enemy.getPlayer().addElectricity(enemy.getGold());
				enemy.getPlayer().addScore(1);
			}

			enemy.isActualDead = true;
			//Delete
			retval = true;
		} else if (enemy.getTileX() == mLevel.getFinishX(enemy.getPath()) && enemy.getTileY() == mLevel.getFinishY(enemy.getPath())) {
            //Enemy pass the map, decrement the mLife
            if(enemy.getPlayer().getLife() > 0)
            	enemy.getPlayer().decrementLife();
            
			retval = true;
		}
		return retval;
	}
	
	protected void deleteEnemies(ArrayList<Enemy> DeletedEnemies) {
		for (int i = 0; i < DeletedEnemies.size(); i++)
			mEnemies.remove(DeletedEnemies.get(i));
		DeletedEnemies.clear();
	}
	
	protected void spawnEnemies(Node[] EnemyData) {
		if (EnemyData != null) {
			GameRenderer.instance().fakeInvalidate();
			
			//While there's still an enemy
			int i = 0;
			while (i < EnemyData.length) {
				//Add to the list
				mEnemies.add(new Enemy(
						EnemyData[i],
						mLevel.getWave(mPlayer.getWave()).getEnemyImages(),
						mLevel,
						-1,
						getPlayer()
				));
				i++;
			}
		}
        
        if (mLevel.getWave(mPlayer.getWave()).isWaveFinished() && mEnemies.isEmpty()){
			//Unload image
			mLevel.getWave(mPlayer.getWave()).unloadEnemyImage();
			
			//Next wave
            mPlayer.nextWave();
            this.save();
            Log.i("this" , "");
            
            if (mPlayer.getWave() == mLevel.getWaveLength()) {
                mPlayer.setWon(true);
                endGame();
            } else {
            	//Load enemy image
				mLevel.getWave(mPlayer.getWave()).loadEnemyImage();
				
				checkDialog();
			}
        }
	}
	
	public void checkDialog() {
		mCurrentDialog = mLevel.getDialog(mPlayer.getWave());
    	if (mCurrentDialog != null) {
    		final String level	= mLevelSource;
    		final int wave		= mPlayer.getWave();
    		mListener.onDialog(level, wave);
    	}
	}
	
	public void checkDialog(int pSequence) {
		mCurrentDialog = mLevel.getDialog(pSequence);
    	if (mCurrentDialog != null) {
    		final String level	= mLevelSource;
    		final int wave		= pSequence;
    		mListener.onDialog(level, wave);
    	}
	}
	
	public void endGame() {
		if (mListener != null) {
			if (mPlayer.hasWon())
				mListener.onWin(mPlayer.getScore());
			else
				mListener.onGameOver();
		}
	}
	
	protected void checkBullets(long time) {
		//For each tower, get new bullets
		mNewBullets.clear();
		for (int i = 0; i < mTowers.size(); i++) {
			//Get array
			Bullet[] bulletArray = mTowers.get(i).getBullets();

			//Add each bullet
			if (bulletArray != null)
				for (int j = 0; j < bulletArray.length; j++) mNewBullets.add(bulletArray[j]);
		}

		//Insert new bullets
		for (int i = 0; i < mNewBullets.size(); i++)
			mBullets.add(mNewBullets.get(i));

		//For each bullet
		mInactiveBullets.clear();
		for (int i = 0; i < mBullets.size(); i++) {
			//Update
			Bullet current = mBullets.get(i);
			current.update(time);

			//If inactive
			if (!current.isActive()) {
				//Create damaged vectors
				ArrayList<Enemy> damaged = new ArrayList<Enemy>();

				//If area, check other enemies
				if (current.getData().getArea() <= 0) damaged.add(current.getTarget());
				else {
					//For each enemy
					long areaSquared = current.getData().getArea() * current.getData().getArea();
					for (int j = 0; j < mEnemies.size(); j++) {
						//Calculate distance to locked
						final int X			= mEnemies.get(j).getX() - current.getTarget().getX();
						final int Y			= mEnemies.get(j).getY() - current.getTarget().getY();
						final long Distance	= (X * X) + (Y * Y);

						//Add to damaged if distance within area
						if (Distance <= areaSquared && current.getTarget().getPlayer().equals(mEnemies.get(j).getPlayer())) damaged.add(mEnemies.get(j));
					}
				}

				//Damage enemies
				for (int j = 0; j < damaged.size(); j++) current.damage(damaged.get(j));

				//Insert to inactive bullets
				mInactiveBullets.add(current);
			}
		}

		//For each dying bullet
		for (int i = 0; i < mInactiveBullets.size(); i++) {
			//Remove
			mBullets.remove(mInactiveBullets.get(i));
		}
	}
	
	protected void checkGoal(long time) {
		//Update timer if exist
		/*if (mTimer != null) {
			mTimer.update(time);

			//If limit, game over
			if (mTimer.isLimit()) {
				Vector Parameter = new Vector();
				Parameter.addElement(new Integer(mCurrentLevel));
				StateManager.instance().goTo(GameState.GAME_OVER, Parameter, true);
			}
		}

		//Check gold
		if (mLevel.getGoldGoal() > 0) {
			if (mPlayer.getGold() >= mLevel.getGoldGoal()) {
				mHasWon = true;
			}
		}*/
	}
	
	protected void checkFloatingText(long time) {
		for (int i = 0; i < mFloatingText.size(); ++i) {
			mFloatingText.get(i).update(time);
			if (!mFloatingText.get(i).isAlive())
				mDeletedFloatingText.add(mFloatingText.get(i));
		}
		
		for (int i = 0; i < mDeletedFloatingText.size(); ++i)
			mFloatingText.remove(mDeletedFloatingText.get(i));
		
		mDeletedFloatingText.clear();
	}
	
	// for multiplayer
	public Player getPlayer() {
		return mPlayer;
	}
	
	// for game touch listener
	Map getMap() {
		return mMap;
	}
	
	public boolean isTileBuildable(int x, int y) {
		return mMap.isTileBuildable(x, y);
	}
	
	public int[] getAvailableTowers() {
		return mLevel.getAvailableTowers();
	}
	
	public String getLevelName() {
		return mLevel.getName();
	}
	
	GameEventListener getListener() {
		return mListener;
	}
	
	public Entity getEntityAt(int x, int y) {
		int size = 0;
		size = mTowers.size();
		for (int i = 0; i < size; ++i) {
			Entity entity = mTowers.get(i);
			if (entity.getTileX() == x && entity.getTileY() == y)
				return entity;
		}
		
		size = mGenerators.size();
		for (int i = 0; i < size; ++i) {
			Entity entity = mGenerators.get(i);
			if (entity.getTileX() == x && entity.getTileY() == y)
				return entity;
		}
		
		size = mEnemies.size();
		for (int i = 0; i < size; ++i) {
			Entity entity = mEnemies.get(i);
			if (entity.getTileX() == x && entity.getTileY() == y)
				return entity;
		}
		
		if (mMap.isNode(x, y)) {
			size = mNodes.size();
			for (int i = 0; i < size; ++i) {
				Entity entity = mNodes.get(i);
				if (entity.getTileX() == x && entity.getTileY() == y)
					return entity;
			}
		}
		
		return null;
	}
	
	protected void addTower(Tower t, boolean force) {
		// check node
		if (!mMap.isNode(t.getTileX(), t.getTileY()) && !force) return;
		if (!t.upgrade(t.getData(), getElapsedGameTime(), true, false) && !force) return;
		
		boolean nearbyGenerator = false;
		
		for (int i = 0; i < mGenerators.size(); ++i) 
			if (mGenerators.get(i).inRange(t)) {
				if (!mGenerators.get(i).isInProgress(getElapsedGameTime()))
					nearbyGenerator = true;
				t.addGenerator(mGenerators.get(i));
			}
		
		if (!nearbyGenerator && !force) return;
		mTowers.add(t);
		//t.getPlayer().addElectricity(-t.getData().getBuildCost());
		afterAddTower(t);
	}
	
	public void addTower(Tower t) { addTower(t , false);}
	
	protected void afterAddTower(Tower t) {}
	
	protected void addGenerator(Generator g, boolean force) {
		if (!mMap.isTileBuildable(g.getTileX(), g.getTileY()) && !force) return;
		if (mMap.isNode(g.getTileX(), g.getTileY()) && !force) return;
		if (!g.upgrade(g.getData(), getElapsedGameTime(), true, false) && !force) return;
		
		// check tower
		for (int i = 0; i < mTowers.size(); ++i) 
			if (g.inRange(mTowers.get(i)))
				mTowers.get(i).addGenerator(g);
		
		mGenerators.add(g);
		afterAddGenerator(g);
	}
	
	public void addGenerator(Generator g) { addGenerator(g, false);}
	
	protected void afterAddGenerator(Generator g) {}
	
	protected void upgradeTower(Tower t, int towerDataIndex, boolean force) {
		TowerData newData = TowerFactory.instance().getTowerData(t.getData().getUpgrade(towerDataIndex));
		if (t.upgrade(newData, getElapsedGameTime(), true, force))
			afterUpgradeTower(t , towerDataIndex);
	}
	
	public void upgradeTower(Tower t, int towerDataIndex) { upgradeTower(t, towerDataIndex, false);}
	
	protected void afterUpgradeTower(Tower t, int towerDataIndex) {}
	
	protected void upgradeGenerator(Generator g, int component, boolean force) {
		GeneratorData newData = null;
		
		if (component == 0)
			newData = GeneratorFactory.instance().getGeneratorData(g.getData().getPowerLevel() + 1, g.getData().getRangeLevel(), g.getData().getVoltageLevel());
		else if (component == 1)
			newData = GeneratorFactory.instance().getGeneratorData(g.getData().getPowerLevel(), g.getData().getRangeLevel() + 1, g.getData().getVoltageLevel());
		else if (component == 2)
			newData = GeneratorFactory.instance().getGeneratorData(g.getData().getPowerLevel(), g.getData().getRangeLevel(), g.getData().getVoltageLevel() + 1);
		
		// kalau ga isa upgrade
		if (newData == null) return;
		
		if (g.upgrade(newData, getElapsedGameTime(), true, force))
			afterUpgradeGenerator(g, component);
	}
	
	public void upgradeGenerator(Generator g, int component) { upgradeGenerator(g, component, false);}
	
	protected void afterUpgradeGenerator(Generator g, int component) {}
	
	protected void dismantleTower(Tower t, boolean force) {
		boolean result = t.dismantle(getElapsedGameTime(), force);
		if (result) {
			mSoldTowers.add(t);
			afterDismantleTower(t);
		}
	}
	
	public void dismantleTower(Tower t) { dismantleTower(t, false);}
	
	protected void afterDismantleTower(Tower t) {}
	
	protected void dismantleGenerator(Generator g, boolean force) {
		/// TODO : bikin tower yang ga ada generator nya offline
		boolean result = g.dismantle(getElapsedGameTime(), force);
		if (result) {
			mSoldGenerators.add(g);
			for (int i = 0; i < mTowers.size(); ++i)
				mTowers.get(i).removeGenerator(g);
			afterDismantleGenerator(g);
		}
	}
	
	public void dismantleGenerator(Generator g) { dismantleGenerator(g , false);}
	
	protected void afterDismantleGenerator(Generator g) {}
	
	public void setListener(GameEventListener listener) {
		mListener = listener;
		if (mPlayer != null) {
			mPlayer.setGameListener(listener);
		}
	}
	
	public void onPause() {
		SoundManager.instance(null).temporarySetMute(true); //Pasti sudah pernah di-instantiate sebelumnya
		mPaused = true;

		if (mCurrentDialog != null) {
			mMap.onPause();
			GameRenderer.instance().invalidateVBO();
		}
	}
	
	public void onResume() {
		SoundManager.instance(null).temporarySetMute(false); //Pasti sudah pernah di-instantiate sebelumnya
		mPaused = false;
		
		mCurrentDialog = null;
	}
	
	public void onFinish() {
		mRunning = false;
		GameRenderer.instance().waitDrawingComplete();
	}
	
	public void update(long time) throws GameException {
		if (!mPlayer.hasWon()) {
			//Update entities
	        checkTower(time);
	        checkGenerator(time);
	        checkEnemy(time);
			checkBullets(time);
			checkNode(time);
			checkFloatingText(time);
	
			//Check victory
			checkGoal(time);
			
			if (mListener == null)
            	throw new GameException("No handler set");
		}
		afterUpdate();
	}
	
	protected void afterUpdate() {}
	
	public void draw(GL10 gl, int width, int height, float scaleX, float scaleY) {
        mMap.draw(gl, mDrawStartX, mDrawStartY, mDrawEndX, mDrawEndY);
        
        //GameRenderer.instance().drawText(gl, "Saya tampan", 100, 100, 5, 0xffff0000);
        
        // add entities for sorting and drawing
        int size = 0;
        size = mTowers.size();
        for (int i = 0; i < size; ++i)
        	mTowers.get(i).draw(gl);
        size = mGenerators.size();
        for (int i = 0; i < size; ++i)
        	mGenerators.get(i).draw(gl);
        size = mEnemies.size();
        for (int i = 0; i < size; ++i)
        	mEnemies.get(i).draw(gl);
        
        //Draw bullets
		for (int i = 0; i < mBullets.size(); i++)
			mBullets.get(i).draw(gl);
		
		// draw floating text
		for (int i = 0; i < mFloatingText.size(); ++i)
			mFloatingText.get(i).draw(gl);
		
		mPlayer.drawCursor(gl);
	}
	
	/**
	 * SAVE GAME STUFFS
	 */
	public void save() {
		if (isSaveable) {
			File saveFile = new File(Utilities.SAVE_FILE);
	
			try {
				FileOutputStream fos = new FileOutputStream(saveFile);
				
				ObjectOutputStream stream = new ObjectOutputStream(fos);
				
				SaveableGame game = createSaveable();
				stream.writeObject(game);
				
				stream.close();
				
				fos.close();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	
	private SaveableGame createSaveable() {
		Log.i("this" , "SAVEABLE GAME");
		SaveableGame retval = new SaveableGame();
		
		retval.mLevelSource = mLevelSource;
		retval.mPlayer = mPlayer.createSaveable();
		
		for (Tower t : mTowers) {
			Log.i("this" , "SAVEABLE TOWER");
			retval.addTower(t.createSaveable());
		}
		
		for (Generator g : mGenerators) {
			retval.addGenerator(g.createSaveable());
		}
		
		return retval;
	}
	
	public String getLevelSource() {
		return mLevelSource;
	}
	
	public void setSaveable(boolean val) {
		isSaveable = val;
	}
	
	protected void afterShowPanel() {}
	
	public enum ActionType {
		ACTION_TOUCH,
		ACTION_STAT_UPDATE,
		ACTION_SHOW_PANEL,
		ACTION_CONSTRUCT,
		ACTION_1,
		ACTION_2,
		ACTION_3,
		ACTION_4
	};
	
	protected Level mLevel;
	private Map		mMap;
	private Dialog	mCurrentDialog;
	private String	mLevelSource;
	
	// game running stuffs
	private boolean mRunning;
	private boolean mPaused;
	protected Player mPlayer;
	private int[] mComponents;
	
	// multiple entities
	protected ArrayList<Tower> 	mTowers;
	protected ArrayList<dp.mobile.entity.Node> mNodes;
	protected ArrayList<Tower>	mSoldTowers;
	protected ArrayList<Tower>	mDeletedTowers;
	protected ArrayList<Generator>mGenerators;	
	protected ArrayList<Generator>mSoldGenerators;
	protected ArrayList<Generator>mDeletedGenerators;
	protected ArrayList<Bullet>	mBullets;
	protected ArrayList<Bullet> mNewBullets;
	protected ArrayList<Bullet> mInactiveBullets;
	protected ArrayList<Enemy>	mEnemies;
	protected ArrayList<Enemy>	mDeletedEnemies;
	
	//floating text
	protected ArrayList<FloatingText> mFloatingText;
	protected ArrayList<FloatingText> mDeletedFloatingText;
	
	//Draw area
	private int mDrawEndX;
	private int mDrawEndY;
	private int mDrawStartX;
	private int mDrawStartY;
	
	protected GameEventListener mListener;
	
	//Gametime
	private long mStartGameTime;
	
	protected boolean isSaveable = true;
	
	//Save game location
	public static final String SAVE_DIR = "saves";
}
